package com.maxlen.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.maxlen.common.to.es.SkuEsModel;
import com.maxlen.search.constant.EsConstant;
import com.maxlen.search.dto.SearchHomeDTO;
import com.maxlen.search.dto.SearchResultDTO;
import com.maxlen.search.service.MallSearchService;
import com.maxlen.search.vo.SearchParamVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    private ElasticsearchClient client;

    @Override
    public SearchResultDTO getSearch(SearchParamVO searchParamVO) {
        try {

            System.out.println(searchParamVO);

            SearchResponse<SkuEsModel> search = sendSearchRequest(searchParamVO);

            System.out.println(search);

            SearchResultDTO searchByPackage = getSearchByPackage(search);

            if(searchParamVO.getPageNum() != null){
                searchByPackage.setPageNum(searchParamVO.getPageNum());
            }else{
                searchByPackage.setPageNum(0);
            }

            return searchByPackage;

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<SearchHomeDTO> getSearchHome(){
        SearchRequest request = SearchRequest.of(builder -> {
            builder.index(EsConstant.PRODUCT_INDEX);
            builder.sort(o -> o
                    .field(v -> v
                        .field(EsConstant.HOT_SCORE)
                        .order(SortOrder.Desc)));
            builder.sort(o -> o
                .field(v -> v
                        .field(EsConstant.SALE_COUNT)
                        .order(SortOrder.Desc)));
            builder.aggregations(EsConstant.CATEGORY_AGG, o -> o
                    .terms(v -> v
                            .field(EsConstant.CATALOG_ID)
                            .size(2)));

            return builder;
        });

        SearchResponse<SkuEsModel> search = null;
        try {
            search = client.search(request, SkuEsModel.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        List<LongTermsBucket> array = search.aggregations().get(EsConstant.CATEGORY_AGG).lterms().buckets().array();


        Long num1 = null;
        Long num2 = null;

        if(array.size() > 0){
             num1 = Long.valueOf(array.get(0).key());
            if(array.size() > 1){
                 num2 = Long.valueOf(array.get(1).key());
            }
        }

        List<SearchHomeDTO> searchHomeDTOList = new ArrayList<>();
        SearchHomeDTO searchHomeDTO;
        if (num1 != null){
            sendSearchByCategoryId(searchHomeDTOList, num1, EsConstant.CATALOG_ID);
        }

        if (num2 != null){
            sendSearchByCategoryId(searchHomeDTOList, num2, EsConstant.CATALOG_ID);
        }

        return searchHomeDTOList;
    }

    // 根据分类 id 获取前 10 条数据
    private void sendSearchByCategoryId(List<SearchHomeDTO> searchHomeDTOList, Long num, String value){
        try {
            SearchHomeDTO searchHomeDTO = new SearchHomeDTO();

            SearchRequest searchRequest = SearchRequest.of(builder -> {
                builder.index(EsConstant.PRODUCT_INDEX);
                builder.query(o -> o
                        .term(v -> v
                                .field(value)
                                .value(num)));
                builder.size(EsConstant.PAGE_SIZE);
                builder.sort(o -> o
                        .field(v -> v
                                .field(EsConstant.HOT_SCORE)
                                .order(SortOrder.Desc)));
                builder.sort(o -> o
                        .field(v -> v
                                .field(EsConstant.SALE_COUNT)
                                .order(SortOrder.Desc)));

                return builder;
            });

            System.out.println(searchRequest);

            SearchResponse<SkuEsModel> search = client.search(searchRequest, SkuEsModel.class);

            List<SkuEsModel> skuEsModelList = search.hits().hits().stream().map((item) -> {
                return item.source();
            }).collect(Collectors.toList());

            searchHomeDTO.setCatalogId(num);
            searchHomeDTO.setCatalogName(skuEsModelList.get(0).getCatalogName());
            searchHomeDTO.setProducts(skuEsModelList);

            searchHomeDTOList.add(searchHomeDTO);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 构建检索请求
    private SearchResponse<SkuEsModel> sendSearchRequest(SearchParamVO searchParamVO) throws IOException {

        // 请求信息
        SearchRequest searchRequest = SearchRequest.of(builderSearch -> {
            builderSearch.index(EsConstant.PRODUCT_INDEX);

            builderSearch.query(q -> q
                    .bool(builder -> {
                        // 关键字查询
                        if (searchParamVO.getKeyword() != null && StringUtils.hasText(searchParamVO.getKeyword())) {
                            builder.must(m -> m
                                    .match(t -> t
                                            .field(EsConstant.SKU_TITLE)
                                            .query(searchParamVO.getKeyword())));
                        }

                        // 三级分类 id 查询
                        if (searchParamVO.getCatelog3Id() != null && !searchParamVO.getCatelog3Id().equals(0L)) {
                            builder.filter(TermQuery.of(m -> m
                                    .field(EsConstant.CATALOG_ID)
                                    .value(searchParamVO.getCatelog3Id()))._toQuery());
                        }

                        // 品牌 id 查询
                        if (searchParamVO.getBrandId() != null && !searchParamVO.getBrandId().isEmpty()) {
                            List<FieldValue> fieldValues = new ArrayList<>();
                            for (Long brandId : searchParamVO.getBrandId()) {
                                fieldValues.add(FieldValue.of(brandId));
                            }
                            builder.filter(TermsQuery.of(m -> m
                                    .field(EsConstant.BRAND_ID)
                                    .terms(v -> v.value(fieldValues)))._toQuery());
                        }

                        // 价格区间
                        if (searchParamVO.getSkuPrice() != null && !searchParamVO.getSkuPrice().isEmpty()) {
                            String[] s = searchParamVO.getSkuPrice().split("_");
                            builder.filter(RangeQuery.of(builder1 -> {
                                builder1.field(EsConstant.SKU_PRICE);
                                if (s.length > 0) {
                                    if (!s[0].isEmpty()) {
                                        builder1.gte(JsonData.of(Double.parseDouble(s[0])));
                                    }
                                    if (s.length > 1 && !s[1].isEmpty()) {
                                        builder1.lte(JsonData.of(Double.parseDouble(s[1])));
                                    }
                                }
                                return builder1;
                            })._toQuery());
                        }

                        // 属性查询
                        if (searchParamVO.getAttrs() != null && !searchParamVO.getAttrs().isEmpty()) {
                            for (String attr : searchParamVO.getAttrs()) {
                                String[] s = attr.split("_");
                                String attrId = s[0];
                                System.out.println(s[0]);
                                String[] split = s[1].split(":");
                                List<FieldValue> fieldValues = new ArrayList<>();
                                for (String s1 : split) {
                                    System.out.println(s1);
                                    fieldValues.add(FieldValue.of(s1));
                                }
                                NestedQuery nestedQuery = NestedQuery.of(n -> n
                                        .path(EsConstant.ATTRS)
                                        .query(u -> u
                                                .bool(BoolQuery.of(builderBool -> {
                                                    builderBool.must(m -> m
                                                            .term(TermQuery.of(p -> p
                                                                    .field(EsConstant.ATTRS_ATTR_ID)
                                                                    .value(attrId))));
                                                    builderBool.must(m -> m
                                                            .terms(TermsQuery.of(p -> p
                                                                    .field(EsConstant.ATTRS_ATTR_VALUE)
                                                                    .terms(v -> v.value(fieldValues)))));
                                                    return builderBool;
                                                }))));
                                builder.filter(nestedQuery._toQuery());
                            }
                        }

                        // 是否有库存
                        if (searchParamVO.getHasStock() != null && searchParamVO.getHasStock()) {
                            builder.filter(TermQuery.of(m -> m
                                    .field(EsConstant.HAS_STOCK)
                                    .value(searchParamVO.getHasStock()))._toQuery());
                        }

                        return builder;
                    }));

            // 排序
            if (searchParamVO.getSort() != null && !searchParamVO.getSort().isEmpty()) {
                String sort = searchParamVO.getSort();
                String[] split = sort.split("_");
                builderSearch.sort(SortOptions.of(m -> m
                        .field(FieldSort.of(builderFieldSort -> {
                            builderFieldSort.field(split[0]);
                            if (split[1].equals(EsConstant.DESC)) {
                                builderFieldSort.order(SortOrder.Desc);
                            } else {
                                builderFieldSort.order(SortOrder.Asc);
                            }
                            return builderFieldSort;
                        }))
                ));

            }

            // 分页
            if (searchParamVO.getPageNum() != null) {
                builderSearch.from(searchParamVO.getPageNum() * EsConstant.PAGE_SIZE);
            }
            builderSearch.size(EsConstant.PAGE_SIZE);

            // 高亮
            if (searchParamVO.getKeyword() != null && StringUtils.hasText(searchParamVO.getKeyword())) {
                builderSearch.highlight(v -> v
                        .fields(EsConstant.SKU_TITLE, HighlightField.of(h -> h
                                .preTags("<span style='color: red'>")
                                .postTags("</span>")))
                );
            }

            // 聚合操作
            Map<String, Aggregation> map = new HashMap<>();

            map.put(EsConstant.BRAND_AGG, Aggregation.of(a -> a
                    .terms(v -> v
                            .field(EsConstant.BRAND_ID)
                            .size(EsConstant.BRAND_SIZE))
                    .aggregations(EsConstant.BRAND_NAME_AGG, Aggregation.of(b -> b
                            .terms(v -> v
                                    .field(EsConstant.BRAND_NAME)
                                    .size(EsConstant.BRAND_SIZE))))
                    .aggregations(EsConstant.BRAND_IMG_AGG, Aggregation.of(b -> b
                            .terms(v -> v
                                    .field(EsConstant.BRAND_IMG)
                                    .size(EsConstant.BRAND_SIZE))))
            ));

            map.put(EsConstant.CATALOG_AGG, Aggregation.of(a -> a
                    .terms(v -> v
                            .field(EsConstant.CATALOG_ID)
                            .size(EsConstant.CATALOG_SIZE))
                    .aggregations(EsConstant.CATALOG_NAME_AGG, Aggregation.of(b -> b
                            .terms(v -> v
                                    .field(EsConstant.CATALOG_NAME)
                                    .size(EsConstant.CATALOG_SIZE))))
            ));

            map.put(EsConstant.ATTR_NESTED_AGG, Aggregation.of(a -> a
                    .nested(v -> v.path(EsConstant.ATTRS))
                    .aggregations(EsConstant.ATTR_AGG, Aggregation.of(b -> b
                            .terms(v -> v
                                    .field(EsConstant.ATTRS_ATTR_ID)
                                    .size(EsConstant.ATTRS_SIZE))
                            .aggregations(EsConstant.ATTR_NAME_AGG, Aggregation.of(c -> c
                                    .terms(v -> v
                                            .field(EsConstant.ATTRS_ATTR_NAME)
                                            .size(EsConstant.ATTRS_SIZE))
                            ))
                            .aggregations(EsConstant.ATTR_VALUE_AGG, Aggregation.of(c -> c
                                    .terms(v -> v
                                            .field(EsConstant.ATTRS_ATTR_VALUE)
                                            .size(EsConstant.ATTRS_SIZE))
                            ))
                    ))
            ));

            builderSearch.aggregations(map);
            return builderSearch;
        });

        System.out.println(searchRequest.toString());

        // 发送请求
        SearchResponse<SkuEsModel> search = client.search(searchRequest, SkuEsModel.class);

        return search;
    }

    // 将查询后得到的数据封装成 SearchResultDTO 返回给前端
    private SearchResultDTO getSearchByPackage(SearchResponse<SkuEsModel> search){
        // 聚合信息封装
        SearchResultDTO searchResultDTO = new SearchResultDTO();

        searchResultDTO.setTotal(search.hits().total().value());

        searchResultDTO.setTotalPages((int) (searchResultDTO.getTotal() / (EsConstant.PAGE_SIZE + 1)));

        List<SkuEsModel> skuEsModelList = new ArrayList<>();

        // 商品信息封装
        List<Hit<SkuEsModel>> hits = search.hits().hits();
        for (Hit<SkuEsModel> hit : hits) {
            SkuEsModel source = hit.source();
            List<String> strings = hit.highlight().get(EsConstant.SKU_TITLE);
            if(strings != null && !strings.isEmpty()){
                System.out.println(strings.get(0));
                source.setSkuTitle(strings.get(0));
            }
            skuEsModelList.add(source);
        }
        searchResultDTO.setProducts(skuEsModelList);

        // Brand 品牌信息封装
        List<LongTermsBucket> brandBucketList = search.aggregations()
                .get(EsConstant.BRAND_AGG)
                .lterms()
                .buckets().array();

        if(!brandBucketList.isEmpty()){
            List<SearchResultDTO.BrandDTO> brandDTOList = new ArrayList<>();
            SearchResultDTO.BrandDTO brandDTO;

            for (LongTermsBucket termsBucket : brandBucketList) {
                brandDTO = new SearchResultDTO.BrandDTO();
                brandDTO.setBrandId(Long.valueOf(termsBucket.key()));

                List<StringTermsBucket> termsBuckets = termsBucket.aggregations().get(EsConstant.BRAND_NAME_AGG).sterms().buckets().array();
                for (StringTermsBucket bucket : termsBuckets) {
                    brandDTO.setBrandName(bucket.key());
                }

                List<StringTermsBucket> termsBuckets1 = termsBucket.aggregations().get(EsConstant.BRAND_IMG_AGG).sterms().buckets().array();
                for (StringTermsBucket bucket : termsBuckets1) {
                    brandDTO.setBrandImg(bucket.key());
                }
                brandDTOList.add(brandDTO);
            }

            searchResultDTO.setBrands(brandDTOList);
        }

        // Catalog 分类信息封装
        List<LongTermsBucket> catalogBucketList = search.aggregations()
                .get(EsConstant.CATALOG_AGG)
                .lterms()
                .buckets().array();

        if(!catalogBucketList.isEmpty()){
            List<SearchResultDTO.CategoryDTO> categoryDTOList= new ArrayList<>();
            SearchResultDTO.CategoryDTO categoryDTO;

            for (LongTermsBucket termsBucket : catalogBucketList) {
                categoryDTO = new SearchResultDTO.CategoryDTO();
                categoryDTO.setCatalogId(Long.valueOf(termsBucket.key()));

                List<StringTermsBucket> termsBuckets = termsBucket.aggregations().get(EsConstant.CATALOG_NAME_AGG).sterms().buckets().array();
                for (StringTermsBucket bucket : termsBuckets) {
                    categoryDTO.setCatalogName(bucket.key());
                }

                categoryDTOList.add(categoryDTO);
            }

            searchResultDTO.setCategorys(categoryDTOList);
        }

        // Attr 属性信息封装
        List<LongTermsBucket> attrBucketList = search.aggregations()
                .get(EsConstant.ATTR_NESTED_AGG)
                .nested().aggregations().get(EsConstant.ATTR_AGG)
                .lterms()
                .buckets().array();

        if(!attrBucketList.isEmpty()){
            List<SearchResultDTO.AttrDTO> attrDTOList = new ArrayList<>();
            SearchResultDTO.AttrDTO attrDTO;

            for (LongTermsBucket termsBucket : attrBucketList) {

                attrDTO = new SearchResultDTO.AttrDTO();
                attrDTO.setAttrId(Long.valueOf(termsBucket.key()));

                List<StringTermsBucket> termsBuckets = termsBucket.aggregations().get(EsConstant.ATTR_NAME_AGG).sterms().buckets().array();
                for (StringTermsBucket bucket : termsBuckets) {
                    attrDTO.setAttrName(bucket.key());
                }

                List<StringTermsBucket> termsBuckets1 = termsBucket.aggregations().get(EsConstant.ATTR_VALUE_AGG).sterms().buckets().array();
//                Set<String> attr = new HashSet<>();
//                for (StringTermsBucket bucket : termsBuckets1) {
//                    String[] split = bucket.key().split(";");
//                    for (String s : Arrays.asList(split)) {
//                        attr.add(s);
//                    }
//                }
//                attrDTO.setAttrValue(attr.stream().collect(Collectors.toList()));
                List<String> attr = new ArrayList<>();
                for (StringTermsBucket stringTermsBucket : termsBuckets1) {
                    attr.add(stringTermsBucket.key());
                }
                attrDTO.setAttrValue(attr);
                attrDTOList.add(attrDTO);
            }

            searchResultDTO.setAttrs(attrDTOList);
        }

        return searchResultDTO;
    }
}
